package jmine.tec.persist.jms;

import javax.jms.JMSException;
import javax.jms.Message;
import javax.transaction.SystemException;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.transaction.jta.JtaTransactionManager;

/**
 * Extende {@link PersistentMessageMDB} e, em caso de erro, sinaliza ao container que a transacao deve tomar um 'rollback'
 * 
 * @author takeshi
 */
public class PersistentMessageCMTMDB extends PersistentMessageMDB {

    private static final Log LOGGER = LogFactory.getLog(PersistentMessageCMTMDB.class);

    protected static final int DEFAULT_MAX_ATTEMPTS = 10;

    private int maxAttempts = -1;

    /**
     * {@inheritDoc}
     */
    @Override
    protected void onError(Message msg, Throwable e) {
        try {
            super.onError(msg, e);
        } finally {
            LOGGER.error("Setting transaction as rollbackOnly", e);
            try {
                int deliveryCount = getDeliveryCount(msg);
                if (deliveryCount < getMaxAttempts()) {
                    rollbackCurrentTransaction();
                } else {
                    LOGGER.warn("Delivery count for message is " + deliveryCount + ". Transaction will not be set roll back only.");
                }
            } catch (Exception ee) {
                LOGGER.error("Error setting transaction as rollbackOnly", ee);
            }
        }
    }

    /**
     * Tenta fazer o rollback da transacao atual
     * 
     * @throws SystemException the exception
     */
    protected void rollbackCurrentTransaction() throws SystemException {
        if (getMessageDrivenContext() != null) {
            getMessageDrivenContext().setRollbackOnly();
        } else {
            JtaTransactionManager txManager = new JtaTransactionManager();
            txManager.afterPropertiesSet();
            txManager.getUserTransaction().setRollbackOnly();
        }
    }

    /**
     * Retorna o número de vezes que a mensagem foi consumida.
     * 
     * @param msg Mensagem
     * @return Número de vezes que a mensagem foi consumida. -1 caso não consiga extrair a informação.
     */
    protected int getDeliveryCount(Message msg) {
        try {
            return msg.getIntProperty("JMSXDeliveryCount");
        } catch (JMSException e) {
            LOGGER.error("Unable to get delivery count from message.", e);
            return -1;
        }
    }

    /**
     * Retorna o número máximo de tentativas de processar mensagens com erro antes de descartá-las.
     * 
     * @return o número máximo de tentativas de processar mensagens com erro antes de descartá-las.
     */
    protected int getMaxAttempts() {
        if (this.maxAttempts == -1) {
            this.maxAttempts = DEFAULT_MAX_ATTEMPTS;
            try {
                if (getBeanFactory().containsBean("mdbMaxAttempts")) {
                    Integer max = (Integer) getBeanFactory().getBean("mdbMaxAttempts");
                    if (max != null) {
                        this.maxAttempts = max;
                    }
                }
            } catch (Exception t) {
                LOGGER.debug("Failed to retrieve max attempt number for messages. Using default value: " + DEFAULT_MAX_ATTEMPTS + ".", t);
            }
        }
        return this.maxAttempts;
    }

}
